home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / scott / WWW / NextStep / Implementation / ParseHTML.h < prev    next >
Text File  |  1993-08-26  |  11KB  |  364 lines

  1. /*                FORMAT CONVERSION FROM SGML
  2. **                ===========================
  3. **
  4. **
  5. **    22 Nov 92    Fixed quoting of hrefs.
  6. **            CERN_WEIRDO ifdefed out -- proper SGML expected
  7. **            REMOVE_SCRIPT ifdefed out -- did ignore lines starting with "."
  8. */
  9.  
  10. #import  "HTStyle.h"
  11. #include "HTML.h"        /* For directory object building */
  12.  
  13. #define PUTC(c) (*targetClass.put_character)(target, c)
  14. #define PUTS(s) (*targetClass.put_string)(target, s)
  15. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  16. #define END(e) (*targetClass.end_element)(target, e)
  17. #define END_TARGET (*targetClass.end_document)(target)
  18. #define FREE_TARGET (*targetClass.free)(target)
  19.  
  20. struct _HTStream {
  21.     CONST HTStreamClass *    isa;
  22.     /* ... */
  23. };
  24.  
  25.  
  26.  
  27. typedef struct _SGML_style {
  28.     char *    start_tag;    /* Tag to mark start of a style     */
  29.     char *    paragraph_tag;    /* Tag to mark paragraph mark within style */
  30.     char *    tab_tag;    /* Tag to mark tab within style     */
  31.     char *    end_tag;    /* Tag to mark end of style         */
  32.     char *    start_text;    /* Text conventionally starting this style */
  33.     char *    paragraph_text;    /* Text used as a paragraph mark within style*/
  34.     char *    end_text;    /* Text used to end a style         */
  35.     HTStyle *    style;        /* Paragraph style to be used         */
  36.     int        free_format;    /* Flag: are line ends word breaks only? */
  37.     int        litteral;    /* Flag: end only at close tag (cheat) ? */
  38. } SGML_style;
  39.  
  40. /*    Stack of previous styles:
  41. */
  42. typedef struct _NestedStyle {
  43.     struct _NestedStyle *    next;        /* previously nested style or 0 */
  44.     SGML_style *         SGML;        /* SGML style interrupted */
  45. } NestedStyle;
  46.  
  47.  
  48. /*    Paragraph Styles used by the SGML parser:
  49. **    ----------------------------------------
  50. */
  51.  
  52. static SGML_style    Normal =
  53.     { "", "<P>\n", "\t", "",
  54.      "","", "", 0 ,1, 0};
  55.     
  56. static SGML_style    Heading[6] = {
  57.     { "\n<H1>", "</H1>\n<H1>", "\t", "</H1>", "", "", "", 0, 1, 0},
  58.     { "\n<H2>", "</H2>\n<H2>", "\t", "</H2>", "", "", "", 0, 1, 0},
  59.     { "\n<H3>", "</H3>\n<H3>", "\t", "</H3>", "", "", "", 0, 1, 0},
  60.     { "\n<H4>", "</H4>\n<H4>", "\t", "</H4>", "", "", "", 0, 1, 0},
  61.     { "\n<H5>", "</H5>\n<H5>", "\t", "</H5>", "", "", "", 0, 1, 0},
  62.     { "\n<H6>", "</H6>\n<H6>", "\t", "</H6>", "", "", "", 0, 1, 0}
  63. };
  64.      
  65. static SGML_style    Glossary =    /* Large hanging indent with tab */
  66.     { "\n<DL>\n<DT>", "\n<DT>", "\n<DD>", "\n</DL>\n",
  67.     "", "", "", 0, 1};
  68.     
  69. static SGML_style    listStyle  =        /* Hanging indent with tab */
  70.     { "\n<UL>\n<LI>", "\n<LI>", "\t", "\n</UL>",
  71.     "\267\t", "\267\t", "", 0, 1, 0};
  72.  
  73. static SGML_style    menuStyle  =        /* Like UL but less gap */
  74.     { "\n<MENU>\n<LI>", "\n<LI>", "\t", "\n</MENU>",
  75.     "\267\t", "\267\t", "", 0, 1, 0};
  76.  
  77. static SGML_style    addressStyle =
  78.     { "\n<ADDRESS>", "<P>", "\t", "\n</ADDRESS>",
  79.     "", "", "", 0, 1, 0 };
  80.     
  81. /*    Explicit format styles:
  82. */    
  83. static SGML_style    Example =    /* Fixed width font, at least 80 chars wide */
  84.     { "\n<XMP>", "\n", "\t", "</XMP>",
  85.     "", "", "", 0 , 0, 1};
  86.  
  87. static SGML_style    Preformatted =    /* Fixed width font, at least 80 chars wide */
  88.     { "\n<PRE>", "\n", "\t", "</PRE>",
  89.     "", "", "", 0 , 0, 0};        /* not litteral */
  90.  
  91. static SGML_style    Fixed =    /* Fixed width font, at least 80 chars wide */
  92.     { "\n<FIXED>", "<P>", "\t", "</FIXED>",
  93.     "", "", "", 0 , 1, 0};
  94.  
  95. static SGML_style    Listing =    /* Fixed width font, at least 132 chars wide */
  96.     { "\n<LISTING>", "\n", "\t", "</LISTING>",
  97.     "", "", "", 0 , 0, 1};
  98.  
  99. /*    Table of all possible SGML paragraph styles
  100. */
  101. static SGML_style * styleTable[] = {
  102.     &Normal, &Heading[0], &Heading[1], &Heading[2],
  103.     &Heading[3], &Heading[4], &Heading[5],
  104.     &Glossary, &listStyle, &menuStyle, &addressStyle,  &Preformatted, &Fixed, &Example, &Listing
  105. }; /* style table */
  106.  
  107. #define NUMBER_OF_STYLES (sizeof(styleTable)/sizeof(styleTable[0]))
  108.  
  109.  
  110. /*        Write SGML File back OUT
  111. **        ------------------------
  112. **
  113. **    This is currently quite NeXT-specific.
  114. **
  115. **    We run through te runs. When a characteristic of a run changes, we
  116. **    output the approporiate SGML code. When several characteristics change at
  117. **    the same place, we output the code in an order such that the resulting
  118. **    structures wil be nested. This means first unwrapping the old ones, and
  119. **    then entering the new ones. For example, it is better to produce
  120. **
  121. **        <h2><a>...</a></h2><a>...</a>
  122. **    than
  123. **
  124. **        <h2><a>...</h2></a><a>...</a>
  125. **
  126. **    The special treatment of newlines is because we want to strip extra newlines
  127. **    out. We ignore newlines at the beginning and end of the para style,
  128. **    and we treat multiple newlines as a single paragraph mark.
  129. **
  130. **    Bugs:    @@@ Highlighting is ignored.
  131. **        @@@ end text is ignored.
  132. */
  133.  
  134. #define LINE_WRAP 64        /* Start thinking about line wrap here */
  135.  
  136. static int SGML_gen_newlines;    /* Number of newlines pending during SGML generation */
  137. static SGML_gen_errors;        /* Number of unrcognizable runs */
  138. static SGML_style * currentSGML;
  139. static const char * saveName;    /* pointer to name node is being saved under */
  140. static char * prefix;        /* Pointer to prefix string to be junked */
  141. static int    lineLength;    /* Number of characters on a line so far */
  142.  
  143. /*    This function, for any paragraph style, finds the SGML style, if any
  144. */
  145. SGML_style * findSGML(void *para)
  146. {
  147.     int i;
  148.     if (!para) return &Normal;            /* Totally unstyled becomes Normal */
  149.     for (i=0; i<NUMBER_OF_STYLES; i++) {
  150.     SGML_style * S = styleTable[i];
  151.     if (S) {
  152.         HTStyle * style = S->style;
  153.         if(style) {
  154.             if (style->paragraph == para)
  155.             return S;
  156.         }
  157.     }
  158.     }
  159.     if (TRACE) printf("HT: Can't find SGML style!\n");
  160.     SGML_gen_errors++; 
  161.     return &Normal;
  162. }
  163.  
  164. /*    Change Run
  165. **    ==========
  166. */
  167. /*    This function generates the code for one run, given the previous run.
  168. **
  169. */
  170. - (void) changeRunFrom: (NXRun *) last to: (NXRun *) r
  171. {
  172.     int chars_left = r->chars;
  173.        
  174.     if (r->info != last->info) {            /* End anchor */
  175.     if (last->info) PUTS ("</A>");
  176.     }
  177.     
  178.     if (r->paraStyle != last->paraStyle)
  179.      if (last->paraStyle) {                /* End paragraph */
  180.     if (currentSGML) PUTS(currentSGML->end_tag);
  181.     else PUTS("<P>\n");
  182.     lineLength = 0;     /* At column 1 */
  183.     }
  184.  
  185.     
  186.     if (r->paraStyle != last->paraStyle) {        /* Start paragraph */
  187.     currentSGML = findSGML(r->paraStyle);
  188.     if (currentSGML) {
  189.  
  190.         if (currentSGML->free_format)
  191.          while(chars_left && WHITE(*read_pointer)) {/* Strip leading */
  192.         (chars_left)--;                /*   white space */
  193.         (void) NEXT_TEXT_CHAR;
  194.         }
  195.         PUTS(currentSGML->start_tag);
  196.         prefix = currentSGML->start_text;
  197.     } 
  198.     SGML_gen_newlines=0;                /* Cancel  */
  199.     }
  200.     
  201.     if (r->info != last->info) {            /* Start anchor */
  202.  
  203.     if (SGML_gen_newlines) {    /* Got anchor, need paragraph separator */
  204.         PUTS(currentSGML->paragraph_tag);
  205.         SGML_gen_newlines=0;    /* paragraph flushed. */
  206.     }
  207.     if (r->info) {
  208.         HTChildAnchor * a = (HTChildAnchor *) r->info;
  209.         HTAnchor * d = HTAnchor_followMainLink((HTAnchor*)a);
  210.         char * this = HTAnchor_address((HTAnchor*)a);
  211.         
  212.         PUTS("<A\nNAME=");
  213.         PUTS(strrchr(this, '#')+1);
  214.         free(this);
  215.         
  216.         if (d) {
  217.             char * absolute = HTAnchor_address(d);
  218.         char * relative = HTRelative(absolute, saveName);
  219.         
  220.         PUTS(" HREF=\"");
  221.         PUTS(relative);
  222.         PUTC('"');
  223.  
  224.         free(relative);
  225.         free(absolute);
  226.         }
  227.         PUTC('>');
  228.     }
  229.     }
  230.  
  231. /*    Now output the textual part of the run
  232. **
  233. **    Within the prefix region (prefix!=0), we discard white space and
  234. **    characters matching *prefix++. Note the prefix string may contain white space.
  235. **
  236. **    The SGML_gen_newlines flag means that newlines have been found. They are
  237. **    not actually implemented unless some more non-white text is found, so that
  238. **    trailing newlines on the end of paragraphs are stripped.
  239. **
  240. **    The line wrapping is primitive in the extreme, as only text characters are
  241. **    counted. In practise it limits the length of any line to a reasonable amount,
  242. **    though this is not guarranteed.
  243. */
  244.     {
  245.     while (chars_left) {    
  246.         char c = NEXT_TEXT_CHAR;
  247.         chars_left--;
  248.         if (prefix) {
  249.             if (*prefix) {
  250.             if (c==*prefix) {
  251.             ++prefix;
  252.             continue;            /* Strip prefix characters */
  253.             }
  254.             if (WHITE(c)) continue;            /* Strip white space */
  255.             if (TRACE) printf(
  256.        "HTML: WARNING: Paragraph prefix incomplete: %i found where %i expected.\n",
  257.                  c, *prefix);
  258.             }
  259.         prefix=0;                /* Prefix is over */
  260.         }
  261.         
  262.         if (c=='\n') {                /* Paragraph Marks:    */
  263.         if (currentSGML->free_format) {
  264.             SGML_gen_newlines++;        /* Just flag it */
  265.             prefix = currentSGML->paragraph_text;
  266.             } else {
  267.             PUTS(currentSGML->paragraph_tag);
  268.         }
  269.         lineLength = 0;     /* At column 1 */
  270.         
  271.         } else {                    /* Not newline */
  272.  
  273.         if (SGML_gen_newlines) {/* Got text, need paragraph separator */
  274.             PUTS(currentSGML->paragraph_tag);
  275.             SGML_gen_newlines=0;    /* paragraph flushed. */
  276.             lineLength = 0;         /* At column 1 */
  277.         }
  278.         if (c=='\t') {
  279.             if (currentSGML) PUTS(currentSGML->tab_tag);
  280.             else PUTC('\t');
  281.         } else {                /* Not tab or newline */
  282.             lineLength ++;    /* @@bug doesn't count entity names */
  283.             if ((currentSGML->free_format)
  284.             &&  (lineLength++ > LINE_WRAP)    /* Wrap lines if we can */
  285.             &&  (c==' ')) {
  286.                 c = '\n';
  287.                 lineLength = 0;
  288.             }
  289.             
  290.             if (currentSGML->litteral) {
  291.                 PUTC(c);
  292.             } else {
  293.             switch(c) {
  294.                 case '<':     PUTS("<"); break;
  295.                 case '&':     PUTS("&"); break;
  296.             default:    PUTC(c); break;
  297.             } /* switch */
  298.             } /* not litteral */
  299.             }
  300.         }
  301.     }
  302.     }
  303.     
  304. } /* changeRunFrom:to: */
  305.  
  306.  
  307.  
  308. /*    This is the body of the SGML output method.
  309. */
  310. - writeSGML:(HTStrunctured *) target relativeTo:(const char *)aName
  311. {
  312.     NXRun * r = theRuns->runs;
  313.     int sor;                /* Character position of start of run */
  314.     NXRun dummy;
  315.     char buffer[64];
  316.     dummy.paraStyle = 0;
  317.     dummy.info = 0;
  318.     dummy.chars = 0;
  319.     
  320.     HTStructuredClass targetClass = *target->isa; /* copy access routines
  321.     
  322. #define PUTC
  323.     
  324.     SGML_gen_newlines=0;        /* Number of newlines read but not inserted */    
  325.     HT = self;
  326.     saveName = aName;
  327.  
  328.     SGML_gen_errors = 0;
  329.     currentSGML = 0;
  330.     prefix = 0;                /* No prefix to junk */
  331.     
  332.     START_INPUT;
  333.     lineLength = 0;            /* Starting in column 1 */
  334.             
  335.     START(HTML_HTML);
  336.     START(HTML_HEAD);
  337.     START(HTML_TITLE);
  338.     PUTS([window title]);
  339.     END(HTML_TITLE);
  340.     
  341.     if (nextAnchorNumber) {
  342.         sprintf(buffer, "\n<NEXTID N=\"z%i\">\n", nextAnchorNumber);
  343.     PUTS(buffer);
  344.     }
  345.     END(HTML_HEAD);
  346.     START(HTML_BODY);
  347.  
  348. /*    Change style tags etc
  349. */
  350.     [self changeRunFrom:&dummy to:r];            /* Start first run */
  351.     
  352.     for (sor=r++->chars; sor<textLength; sor=sor+(r++)->chars)  {
  353.         if (TRACE) printf("%4i:  %i chars in run %3i.\n",
  354.             sor, r->chars, r-theRuns->runs);
  355.     [self changeRunFrom:r-1 to: r];    /* Runs 2 to N */
  356.     }
  357.     [self changeRunFrom:r to:&dummy];            /* Close last run */
  358.  
  359.     tFlags.changeState = 0;         /* Please notify delegate if changed */
  360.     END(HTML_BODY);
  361.     END(HTML_HTML);
  362.     return (SGML_gen_errors) ? nil : self;    
  363. }
  364.